package com.example.demo.dao.impl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Component;

import com.example.demo.dao.PageRequestWrapper;
import com.example.demo.dao.Selectable;


@Component
public class DefaultSelectable implements Selectable {
	
	@Autowired
    private NamedParameterJdbcTemplate jdbcTemplate;

    @Override
    public <T> Page<T> select(Class<T> type, String limitSql, Map<String, ?> params,
                              int pageNum, int pageSize, long total) {
        List<T> list = pageQuery(type, limitSql, params, pageNum, pageSize);
        return Pages.create(list, pageNum, pageSize, total);
    }

    @Override
    public <T> Page<T> select(Class<T> type, String limitSql, String countSql,
                              Map<String, ?> params, int pageNum, int pageSize) {
        return select(type, limitSql, countSql, params, params, pageNum, pageSize);
    }

    @Override
    public <T> Page<T> select(Class<T> type, String limitSql, String countSql,
                              Map<String, ?> params, Map<String, ?> countParams,
                              int pageNum, int pageSize) {
        long total = count(countSql, countParams);
        if (total == 0L) {
            return Pages.create(new ArrayList<T>(0), pageNum, pageSize, total);
        }
        return select(type, limitSql, params, pageNum, pageSize, total);
    }

    @Override
    public Page<Map<String, Object>> select(String sql, Map<String, ?> params,
                                            int pageNum, int pageSize) {
        String countSql = String.format("SELECT COUNT(*) FROM (%s) t", sql);
        long total = count(countSql, params);
        if (total == 0L) {
            return Pages.create(new ArrayList<Map<String, Object>>(0), pageNum, pageSize, total);
        }
        //前置查询参数，total <= pageSize时虽然无需用到offset和pageSize分页参数，但sql中已有分页参数，没有加入该参数执行SQL会报错
        Map<String, Object> allParams = new HashMap<String, Object>(params);
        allParams.put("offset", pageSize * (pageNum - 1));
        allParams.put("pageSize", pageSize);
        if (total <= pageSize) {
            List<Map<String, Object>> content = select(sql, allParams);
            return Pages.create(content, pageNum, pageSize, total);
        }
        //TODO：Limit语句应由调用端传入，拼接limit语句将影响效率
        String limitSql = String.format(
                "SELECT * FROM (%s) t limit :offset, :pageSize", sql);
        List<Map<String, Object>> content = select(limitSql, allParams);
        return Pages.create(content, pageNum, pageSize, total);
    }

    @Override
    public Page<Map<String, Object>> select(String limitSql, Map<String, ?> params,
                                            int pageNum, int pageSize, long total) {
        if (total == 0L) {
            return Pages.create(new ArrayList<Map<String, Object>>(0), pageNum, pageSize, total);
        }
        //前置查询参数，total <= pageSize时虽然无需用到offset和pageSize分页参数，但sql中已有分页参数，没有加入该参数执行SQL会报错
        Map<String, Object> allParams = new HashMap<String, Object>(params);
        allParams.put("offset", pageSize * (pageNum - 1));
        allParams.put("pageSize", pageSize);
        if (total <= pageSize) {
            List<Map<String, Object>> content = select(limitSql, allParams);
            return Pages.create(content, pageNum, pageSize, total);
        }
        List<Map<String, Object>> content = select(limitSql, allParams);
        return Pages.create(content, pageNum, pageSize, total);
    }

    @Override
    public Page<Map<String, Object>> select(String limitSql, String countSql,
                                            Map<String, ?> params, int pageNum,
                                            int pageSize) {
        return select(limitSql, countSql, params, params, pageNum, pageSize);
    }

    @Override
    public Page<Map<String, Object>> select(String limitSql, String countSql,
                                            Map<String, ?> params,
                                            Map<String, ?> countParams,
                                            int pageNum, int pageSize) {
        long total = count(countSql, countParams);
        return select(limitSql, params, pageNum, pageSize, total);
    }

    @Override
    public <T> List<T> select(Class<T> type, String sql, Map<String, ?> params) {
       // return this.jdbcOperations.query(sql, params, new BeanPropertyRowMapper<T>(type));
    	  return this.jdbcTemplate.query(sql, params, new BeanPropertyRowMapper<T>(type));
    }

    @Override
    public List<Map<String, Object>> select(String sql, Map<String, ?> params) {
        return this.jdbcTemplate.queryForList(sql, params);
    }

    @Override
    public <T> T selectOne(Class<T> type, String sql, Map<String, ?> params) {
        return this.jdbcTemplate.queryForObject(sql, params, type);
    }

    @Override
    public long count(String sql, Map<String, ?> params) {
        Long total = jdbcTemplate.queryForObject(sql, params, Long.class);
        if(total == null) {
            return 0L;
        }
        return total;
    }
    private <T> List<T> pageQuery(Class<T> type, String sql,
                                  Map<String, ?> params, int pageNum, int pageSize) {
        Map<String, Object> allParams = new HashMap<String, Object>(params);
        PageRequestWrapper pageRequest = new PageRequestWrapper(pageNum, pageSize);
        allParams.put("offset", pageRequest.getOffset());
        allParams.put("pageSize", pageSize);
        return jdbcTemplate.query(sql, allParams,
                new BeanPropertyRowMapper<T>(type));
    }

    private static class Pages {
        public static <T> Page<T> create(List<T> content, int pageNum,
                                         int pageSize, long total) {
            return new PageImpl<T>(content, new PageRequestWrapper(pageNum, pageSize),
                    total);
        }
    }
}
